#include "formula.h"

Formula::Formula() {
    elements = new LinkedList<FormulaElement>();
    fitness=1000000000000000000000000000.0;
    wasNeverNan=true;
}

Formula::~Formula() {
    delete elements;
}

std::string& Formula::toString() {
    if(!elements)
        return *(new std::string("null"));
    std::string *returnString = new std::string();
    elements->resetIteratorPosition();
    bool addSpace = false;
    while(elements->iteratorHasNext()) {
        if(addSpace)
            returnString->append(" ");
        else
            addSpace=true;
        std::string& elementString = elements->iteratorGetNext().toString();
        returnString->append(elementString);
        delete &elementString;
    }
    return *returnString;
}

void Formula::evaluate(InputIterator& myInputIterator) {
    assert(invariant(false));
    assert(elements->getSize()>0);
    assert(myInputIterator.wasReseted());
    fitness=0;
    LinkedList<double> *evaluationStack = new LinkedList<double>();
    while(myInputIterator.hasNext() && wasNeverNan) {
        fitness+=evaluatePosition(myInputIterator.getNext(), evaluationStack);
    }
    delete evaluationStack;
    assert(invariant(false));
}

const double& Formula::getFitness() {
    return fitness;
}

void Formula::mutate() {
    assert(invariant(false));
    assert(elements->getSize()>0);
	int mutateLevel=(rand()>>24);	// ~RAND_MAX=63 (0 bis u mit 63)
	int level=0;					// local level
	int chance;

	elements->resetIteratorPosition();
	while(elements->iteratorHasNext()) {  // Iteration over all the Elements in the Formula
	    assert(invariant(true));
        chance=(rand()>>26);		      // ~RAND_MAX=31 (0 bis u. mit 31)
        if(chance>mutateLevel) {          // Here we decided to mutate. Now we have to choose how we mutate:
            chance=(rand()>>25);		      // ~RAND_MAX=31 (0 bis u. mit 63)
            if(chance<10) {
                // add new element
                mutateAddNewElementAtIteratorPosition(chance, level, elements->iteratorPositionIsCloseToStart());
                assert(invariant(true));
                assert(invariantSpecialMutationLevelTest(level));
            }
            else if(chance<20) {
                // delete one
                mutateDeleteElementAtIteratorPosition(chance, level);
                assert(invariant(true));
                assert(invariantSpecialMutationLevelTest(level));
            }
            else {
                // element mutate
                mutateElementMutate();
                assert(invariant(true));
                assert(invariantSpecialMutationLevelTest(level));
            }
        }
        else {
            if(elements->iteratorHasNext())
                elements->iteratorGetNext();
                assert(invariant(true));
                assert(invariantSpecialMutationLevelTest(level));
        }
    }
    assert(invariant(true));

    // bring level to the correct value
    mutateBringLevelBackToZero(level);
    assert(invariant(false));
}

bool Formula::invariant(bool isInbetweenMutation) {
    if(!isInbetweenMutation) {
        assert((elements->getSize())%2==1);
        assert(!elements->getElementAtEnd().isNumber());
    }
    int level=0;
    if(isInbetweenMutation)
        elements->saveIteratorPositionNow();
    elements->resetIteratorPosition();
    while (elements->iteratorHasNext()) {
        if(elements->iteratorGetNext().isNumber())
            ++level;
        else {
            assert(level>1);
            --level;
        }
        assert(level>0);
    }
    if(isInbetweenMutation)
        elements->loadOldIteratorPosition();
    else {
        assert(level==1);
    }
    return true;
}

Formula* Formula::copy() {
    Formula* returnFormula=new Formula();
    elements->resetIteratorPosition();
    while (elements->iteratorHasNext()) {
        returnFormula->getListOfFormulaElements().addAtEnd(elements->iteratorGetNext().copy());
    }
    returnFormula->wasNeverNan=wasNeverNan;
    returnFormula->setFitness(fitness);
    return returnFormula;
}

bool operator<(Formula& f1, Formula& f2) {
    return f1.getFitness()<f2.getFitness();
}

bool operator>(Formula& f1, Formula& f2) {
    return f1.getFitness()>f2.getFitness();
}

std::string& Formula::toHumanString() {
    if(!elements)
        return *(new std::string("null"));
    LinkedList<std::string>& toStringStack=*(new LinkedList<std::string>());
    elements->resetIteratorPosition();
    while(elements->iteratorHasNext()) {
        elements->iteratorGetNext().createHumanReadeableToString(toStringStack);
    }
    assert(toStringStack.getSize()==1);
    std::string& returnString = toStringStack.pop();
    delete &toStringStack;
    return returnString;
}

bool Formula::invariantSpecialMutationLevelTest(const int& level) {
    int testLevel=0;
    elements->saveIteratorPositionNow();
    elements->resetIteratorPosition();
    while (elements->iteratorHasNext()) {
        if(elements->iteratorGetNext().isNumber())
            ++testLevel;
        else {
            assert(testLevel>1);
            --testLevel;
        }
        assert(testLevel>0);
    }
    elements->loadOldIteratorPosition();
    assert(level==(testLevel-1));
    return true;
}
